﻿namespace System.Data.Entity.Extensions.UnitOfWork
{
    /// <summary>
    /// 
    /// </summary>
    public class DbContextUnitOfWork : IDbContextUnitOfWork
    {        
        #region Constructor 

        /// <summary>
        /// 
        /// </summary>
        /// <param name="context"></param>
        public DbContextUnitOfWork(DbContext context)
        {
            this._context = context;
        }             
        
        #endregion

        #region IDisposable Implementation

        private bool _disposed;

        /// <summary>
        /// 
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    if (this._context != null)
                    {
                        this._context.Dispose();
                    }
                }

                _disposed = true;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        ~DbContextUnitOfWork()
        {
            Dispose(false);
        }

        #endregion

        #region IUnitOfWork Implementation

        #region Context 

        /// <summary>
        /// 
        /// </summary>
        private readonly DbContext _context;

        /// <summary>
        /// 
        /// </summary>
        public DbContext Context
        {
            get
            {
                return this._context;
            }
        }

        #endregion

        #region Commit

        /// <summary>
        /// 
        /// </summary>
        public event AccessEventHandler CanCommit;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnCanCommit(AccessEventArgs e)
        {
            AccessEventHandler handler = this.CanCommit;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public event CommitEventHandler Committing;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnCommitting(CommitEventArgs e)
        {
            CommitEventHandler handler = this.Committing;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public virtual int Commit()
        {
            try
            {
                if (this.CheckAccess(this.OnCanCommit))
                {
                    this.OnCommitting(new CommitEventArgs());

                    int i = this.Context.SaveChanges();

                    this.OnCommitted(new CommitEventArgs());

                    return i;
                }
                else
                {
                    throw new AccessException("CanCommit");
                }
            }
            catch (Exception)
            {
                this.OnCommittingFailed(new CommitEventArgs());
                throw;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public event CommitEventHandler CommittingFailed;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnCommittingFailed(CommitEventArgs e)
        {
            CommitEventHandler handler = this.CommittingFailed;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public event CommitEventHandler Committed;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnCommitted(CommitEventArgs e)
        {
            CommitEventHandler handler = this.Committed;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        #endregion

        #region Helper

        /// <summary>
        /// 
        /// </summary>
        /// <param name="accessAction"></param>
        /// <returns></returns>
        /// <exception cref="System.ArgumentNullException"></exception>
        protected bool CheckAccess(Action<AccessEventArgs> accessAction)
        {
            if (accessAction == null)
            {
                throw new ArgumentNullException("accessAction");
            }

            bool haveAccess = true;
            AccessEventArgs accessEventArgs = new AccessEventArgs(haveAccess);

            try
            {
                accessAction.Invoke(accessEventArgs);
            }
            catch (Exception)
            {
                throw;
            }

            return accessEventArgs.Result;
        }

        #endregion

        #endregion
    }
}
